using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using Blank.Http.Models;
using Blank.Http.Enumrator;
using Newtonsoft.Json.Linq;
using System.Threading;
namespace Blank.Http
{
    public class HttpClient
    {
        private byte[] bytes = null;
        private Encoding encoding = Encoding.Default;
        private Encoding postencoding = Encoding.Default;
        private HttpWebRequest request = null;
        private HttpWebResponse response = null;
        public HttpClient()
        {
        }
        public bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        {
            return true;
        }
        public HttpResult GetHtml(HttpItem item)
        {
            HttpResult statusCode = new HttpResult();
            try
            {
                this.SetRequest(item);
            }
            catch (Exception exception1)
            {
                statusCode.Cookie = string.Empty;
                statusCode.Header = null;
                statusCode.Html = exception1.Message;
                statusCode.StatusDescription = string.Concat("配置参数时出错：", exception1.Message);
                return statusCode;
            }
            try
            {
                using (this.response = (HttpWebResponse)this.request.GetResponse())
                {
                    SetResponseResult(item, statusCode);
                }

            }
            catch (WebException webException)
            {
                this.response = (HttpWebResponse)webException.Response;
                statusCode.Html = webException.Message;
                if (this.response != null)
                {
                    statusCode.StatusCode = this.response.StatusCode;
                    statusCode.StatusDescription = this.response.StatusDescription;
                }
            }
            catch (Exception exception)
            {
                statusCode.Html = exception.Message;
            }
            if (item.IsToLower)
            {
                statusCode.Html = statusCode.Html.ToLower();
            }
            return statusCode;
        }

        private void SetResponseResult(HttpItem item, HttpResult statusCode)
        {
            #region MyRegion
            statusCode.StatusCode = this.response.StatusCode;
            statusCode.StatusDescription = this.response.StatusDescription;
            statusCode.Header = this.response.Headers;

            if (this.response.Cookies != null)
            {
                statusCode.CookieCollection = this.response.Cookies;
            }
            if (this.response.Headers["set-cookie"] != null)
            {
                statusCode.Cookie = this.response.Headers["set-cookie"];
            }
            using (MemoryStream memoryStream = new MemoryStream())
            {
                if ((this.response.ContentEncoding == null) || !this.response.ContentEncoding.Equals("gzip", StringComparison.InvariantCultureIgnoreCase))
                {
                    using (Stream stream = this.response.GetResponseStream())
                    {
                        stream.CopyTo(memoryStream, 10240);
                    }
                }
                else
                {
                    using (GZipStream gzipStream = new GZipStream(this.response.GetResponseStream(), CompressionMode.Decompress))
                    {
                        gzipStream.CopyTo(memoryStream, 10240);
                    }
                }
                bytes = memoryStream.ToArray();
                if (bytes == null || bytes.Length == 0)
                {
                    statusCode.Html = "本次请求并未返回任何数据";
                }
                else
                {
                    if (item.ResultType == ResultType.Byte)
                    {
                        statusCode.ResultByte = bytes;
                    }
                    if (this.encoding == null)
                    {
                        string result = Encoding.Default.GetString(bytes);
                        Match match = Regex.Match(result, "<meta([^<]*)charset=([^<]*)[\"']", RegexOptions.IgnoreCase);

                        string charset = (match.Groups.Count > 1 ? match.Groups[2].Value.ToLower().Trim() : string.Empty);
                        #region 获取和设置当前请求页面的编码格式
                        if (charset.Length > 2)
                        {
                            try
                            {
                                if (charset.IndexOf(" ") > 0)
                                {
                                    charset = charset.Substring(0, charset.IndexOf(" "));
                                }
                                this.encoding = Encoding.GetEncoding(charset.Replace("\"", "").Replace("'", "").Replace(";", "").Replace("iso-8859-1", "gbk").Trim());
                            }
                            catch
                            {
                                if (!string.IsNullOrEmpty(this.response.CharacterSet))
                                {
                                    this.encoding = Encoding.GetEncoding(this.response.CharacterSet);
                                }
                                else
                                {
                                    this.encoding = Encoding.UTF8;
                                }
                            }
                        }
                        else if (!string.IsNullOrEmpty(this.response.CharacterSet))
                        {
                            this.encoding = Encoding.GetEncoding(this.response.CharacterSet);
                        }
                        else
                        {
                            this.encoding = Encoding.UTF8;
                        }
                        #endregion
                    }
                    statusCode.Html = this.encoding.GetString(bytes);
                }
            }
            #endregion
        }
        private void SetCer(HttpItem item)
        {
            if (string.IsNullOrEmpty(item.CerPath))
            {
                this.request = (HttpWebRequest)WebRequest.Create(item.URL);
                this.SetCerList(item);
            }
            else
            {
                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(this.CheckValidationResult);
                this.request = (HttpWebRequest)WebRequest.Create(item.URL);
                this.SetCerList(item);
                this.request.ClientCertificates.Add(new X509Certificate(item.CerPath));
            }
        }
        private void SetCerList(HttpItem item)
        {
            if ((item.ClentCertificates == null ? false : item.ClentCertificates.Count > 0))
            {
                foreach (X509Certificate clentCertificate in item.ClentCertificates)
                {
                    this.request.ClientCertificates.Add(clentCertificate);
                }
            }
        }
        private void SetCookie(HttpItem item)
        {
            if (item.ResultCookieType == ResultCookieType.String)
            {
                if (!string.IsNullOrEmpty(item.Cookie))
                {
                    this.request.Headers[HttpRequestHeader.Cookie] = item.Cookie;
                }
            }
            else if (item.ResultCookieType == ResultCookieType.CookieCollection)
            {
                this.request.CookieContainer = new CookieContainer();
                if ((item.CookieCollection == null ? false : item.CookieCollection.Count > 0))
                {
                    this.request.CookieContainer.Add(item.CookieCollection);
                }
            }
        }
        private void SetPostData(HttpItem item)
        {
            if (this.request.Method.Trim().ToLower().Contains("post"))
            {
                if (item.PostEncoding != null)
                {
                    this.postencoding = item.PostEncoding;
                }
                byte[] postdataByte = null;
                if (item.PostDataType == PostDataType.Byte && item.PostdataByte != null && item.PostdataByte.Length > 0)
                {
                    postdataByte = item.PostdataByte;
                }
                else if (item.PostDataType == PostDataType.FilePath && !string.IsNullOrEmpty(item.Postdata))
                {
                    using (StreamReader streamReader = new StreamReader(item.Postdata, this.postencoding))
                    {
                        postdataByte = this.postencoding.GetBytes(streamReader.ReadToEnd());
                    }
                }
                else if (!string.IsNullOrEmpty(item.Postdata))
                {
                    postdataByte = this.postencoding.GetBytes(item.Postdata);
                }
                if (postdataByte != null)
                {
                    this.request.ContentLength = (long)(postdataByte.Length);
                    this.request.GetRequestStream().Write(postdataByte, 0, postdataByte.Length);
                }
            }
        }
        private void SetProxy(HttpItem item)
        {
            if (!string.IsNullOrEmpty(item.ProxyIp))
            {
                WebProxy webProxy = null;
                if (!item.ProxyIp.Contains(":"))
                {
                    webProxy = new WebProxy(item.ProxyIp, false);
                }
                else
                {
                    string[] strArrays = item.ProxyIp.Split(':');
                    webProxy = new WebProxy(strArrays[0].Trim(), Convert.ToInt32(strArrays[1].Trim()));
                }
                webProxy.Credentials = new NetworkCredential(item.ProxyUserName, item.ProxyPwd);
                this.request.Proxy = webProxy;
                this.request.Credentials = CredentialCache.DefaultCredentials;
            }
            else if (item.WebProxy != null)
            {
                this.request.Proxy = item.WebProxy;
            }
        }
        private void SetRequest(HttpItem item)
        {
            this.SetCer(item);
            if (item.Header != null && item.Header.Count > 0)
            {
                string[] allKeys = item.Header.AllKeys;
                for (int i = 0; i < (int)allKeys.Length; i++)
                {
                    string str = allKeys[i];
                    this.request.Headers.Add(str, item.Header[str]);
                }
            }
            this.SetProxy(item);
            if (item.ProtocolVersion != null)
            {
                this.request.ProtocolVersion = item.ProtocolVersion;
            }
            this.request.ServicePoint.Expect100Continue = item.Expect100Continue;
            this.request.Method = item.Method;
            this.request.Timeout = item.Timeout;
            this.request.KeepAlive = item.KeepAlive;
            this.request.ReadWriteTimeout = item.ReadWriteTimeout;
            if (!string.IsNullOrEmpty(item.Host))
            {
                this.request.Host = item.Host;
            }
            this.request.Accept = item.Accept;
            this.request.ContentType = item.ContentType;
            this.request.UserAgent = item.UserAgent;
            this.encoding = item.Encoding;
            this.SetCookie(item);
            this.request.Referer = item.Referer;
            this.request.AllowAutoRedirect = item.Allowautoredirect;
            this.SetPostData(item);
            if (item.Connectionlimit > 0)
            {
                this.request.ServicePoint.ConnectionLimit = item.Connectionlimit;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <param name="OnGetResponseCompleted"></param>
        /// <param name="webTimeOut"></param>
        public void BeginGetHttpResult(HttpItem item, Action<object, HttpItem, HttpResult> OnGetResponseCompleted, int webTimeOut = 12000)
        {
            if (item != null)
            {
                //返回参数
                HttpResult result = new HttpResult();
                try
                {
                    //准备参数
                    SetRequest(item);
                }
                catch (Exception ex)
                {
                    result = new HttpResult()
                    {
                        Cookie = string.Empty,
                        Header = null,
                        Html = ex.Message,
                        StatusDescription = "配置参数时出错：" + ex.Message
                    };
                }
                object[] asyncResults = new object[] { request, item, result, OnGetResponseCompleted };
                request.BeginGetResponse(new AsyncCallback(EndGetHttpResult), asyncResults);
                WaitCallback callBack = delegate(object obj)
                {

                    Thread.Sleep(webTimeOut);
                    HttpWebRequest request1 = obj as HttpWebRequest;
                    if (obj != null)
                    {
                        try
                        {
                            request1.Abort();
                        }
                        catch
                        {
                        }
                    }
                };
                ThreadPool.QueueUserWorkItem(callBack, request);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="asyncResult"></param>
        private void EndGetHttpResult(IAsyncResult asyncResult)
        {
            object[] asyncStates = asyncResult.AsyncState as object[];
            HttpWebResponse response = null;
            HttpWebRequest request = null;
            HttpItem item = null;
            HttpResult result = null;
            Action<object, HttpItem, HttpResult> callback = null;
            try
            {
                request = asyncStates[0] as HttpWebRequest;
                item = asyncStates[1] as HttpItem;
                result = asyncStates[2] as HttpResult;
                callback = asyncStates[3] as Action<object, HttpItem, HttpResult>;
                #region 得到请求的response
                using (response = (HttpWebResponse)request.EndGetResponse(asyncResult))
                {
                    result.StatusCode = response.StatusCode;
                    result.StatusDescription = response.StatusDescription;
                    result.Header = response.Headers;
                    if (response.Cookies != null)
                        result.CookieCollection = response.Cookies;
                    if (response.Headers["set-cookie"] != null)
                        result.Cookie = response.Headers["set-cookie"];
                    byte[] ResponseByte = null;
                    using (MemoryStream _stream = new MemoryStream())
                    {
                        //GZIIP处理
                        if (response.ContentEncoding != null && response.ContentEncoding.Equals("gzip", StringComparison.InvariantCultureIgnoreCase))
                        {
                            //开始读取流并设置编码方式
                            new GZipStream(response.GetResponseStream(), CompressionMode.Decompress).CopyTo(_stream, 10240);
                        }
                        else
                        {
                            //开始读取流并设置编码方式
                            response.GetResponseStream().CopyTo(_stream, 10240);
                        }
                        //获取Byte
                        ResponseByte = _stream.ToArray();
                    }
                    if (ResponseByte != null & ResponseByte.Length > 0)
                    {
                        //是否返回Byte类型数据
                        if (item.ResultType == ResultType.Byte)
                            result.ResultByte = ResponseByte;
                        //从这里开始我们要无视编码了
                        if (encoding == null)
                        {
                            Match meta = Regex.Match(Encoding.Default.GetString(ResponseByte), "<meta([^<]*)charset=([^<]*)[\"']", RegexOptions.IgnoreCase);
                            string c = (meta.Groups.Count > 1) ? meta.Groups[2].Value.ToLower().Trim() : string.Empty;
                            if (c.Length > 2)
                            {
                                try
                                {
                                    if (c.IndexOf(" ") > 0)
                                        c = c.Substring(0, c.IndexOf(" "));
                                    encoding = Encoding.GetEncoding(c.Replace("\"", "").Replace("'", "").Replace(";", "").Replace("iso-8859-1", "gbk").Trim());
                                }
                                catch
                                {
                                    if (string.IsNullOrEmpty(response.CharacterSet))
                                        encoding = Encoding.UTF8;
                                    else encoding = Encoding.GetEncoding(response.CharacterSet);
                                }
                            }
                            else
                            {
                                if (string.IsNullOrEmpty(response.CharacterSet))
                                    encoding = Encoding.UTF8;
                                else encoding = Encoding.GetEncoding(response.CharacterSet);
                            }
                        }
                        //得到返回的HTML
                        result.Html = encoding.GetString(ResponseByte);
                    }
                    else
                    {
                        //得到返回的HTML
                        result.Html = "本次请求并未返回任何数据";
                    }
                }
                #endregion
            }
            catch (WebException ex)
            {
                //这里是在发生异常时返回的错误信息
                response = (HttpWebResponse)ex.Response;
                result.Html = ex.Message;
                if (response != null)
                {
                    result.StatusCode = response.StatusCode;
                    result.StatusDescription = response.StatusDescription;
                }
            }
            catch (Exception ex)
            {
                result.Html = ex.Message;
            }
            finally
            {
                if (request != null)
                {
                    request.Abort();
                }
            }
            if (item.IsToLower)
                result.Html = result.Html.ToLower();
            //具体回调传参
            if (callback != null)
                callback(this, item, result);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="OnGetHttpResultCompleted"></param>
        /// <param name="prepareHttpItem"></param>
        public static void GetHttpResultAsync(Action<HttpItem> prepareHttpItem, Action<HttpItem, HttpResult> OnGetResponseCompleted)
        {
            HttpClient http = new HttpClient();
            HttpItem item = new HttpItem()
            {
                Method = "get"
            };
            if (prepareHttpItem != null)
                prepareHttpItem(item);
            Action<object, HttpItem, HttpResult> OnGetHttpResultCompleted = (object sender, HttpItem httpItem, HttpResult httpResult) =>
            {
                if (OnGetResponseCompleted != null)
                    OnGetResponseCompleted(httpItem, httpResult);
            };
            http.BeginGetHttpResult(item, OnGetHttpResultCompleted);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="OnGetHttpResultCompleted"></param>
        /// <param name="prepareHttpItem"></param>
        public static void GetHttpResultAsync(Action<HttpItem> prepareHttpItem, Action<HttpClient, HttpItem, HttpResult> OnGetResponseCompleted)
        {
            HttpClient http = new HttpClient();
            HttpItem item = new HttpItem()
            {
                Method = "get"
            };
            if (prepareHttpItem != null)
                prepareHttpItem(item);
            Action<object, HttpItem, HttpResult> OnGetHttpResultCompleted = (object sender, HttpItem httpItem, HttpResult httpResult) =>
                {
                    if (OnGetResponseCompleted != null)
                        OnGetResponseCompleted((HttpClient)sender, httpItem, httpResult);
                };
            http.BeginGetHttpResult(item, OnGetHttpResultCompleted);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="prepareHttpItem"></param>
        /// <param name="analysisFunc">具体对Html 进行解析的方法</param>
        /// <returns></returns>
        public static ServiceResult<T> GetHttpResult<T>(Action<HttpItem> prepareHttpItem,
            Func<HttpItem, HttpResult, ServiceResult<T>> analysisFunc)
        {
            ServiceResult<T> result = new ServiceResult<T>();
            try
            {
                HttpClient http = new HttpClient();
                HttpItem item = new HttpItem()
                {
                    Method = "get"
                };
                if (prepareHttpItem != null)
                    prepareHttpItem(item);
                HttpResult httpResult = http.GetHtml(item);
                result.HttpResult = httpResult;
                if (httpResult.StatusCode == HttpStatusCode.OK)
                {
                    if (analysisFunc != null)
                        result = analysisFunc(item, httpResult);
                }
            }
            catch (Exception ex)
            {
                result.Exception = ex;
            }
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="prepareHttpItem"></param>
        /// <param name="analysisFunc">具体对Html 进行解析的方法</param>
        /// <returns></returns>
        public static ServiceResult<T> GetJsonResult<T>(Action<HttpItem> prepareHttpItem,
            Func<string, bool> checkError, string tagName = "")
        {
            ServiceResult<T> result = new ServiceResult<T>();
            try
            {
                HttpClient http = new HttpClient();
                HttpItem item = new HttpItem()
                {
                    Method = "get",
                    ContentType = "application/json,*/*"
                };
                if (prepareHttpItem != null)
                    prepareHttpItem(item);
                HttpResult httpResult = http.GetHtml(item);
                result.HttpResult = httpResult;
                if (httpResult.StatusCode == HttpStatusCode.OK)
                {
                    if (checkError != null)
                    {
                        if (checkError(httpResult.Html))
                        {
                            result.Results = httpResult.Html.JsonToObject<T>();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                result.Exception = ex;
            }
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="prepareHttpItem"></param>
        /// <param name="analysisFunc">具体对Html 进行解析的方法</param>
        /// <returns></returns>
        public static ServiceResult<T> GetJsonResult<T>(Action<HttpItem> prepareHttpItem,
            Func<HttpResult, bool> checkError, string tagName = "")
        {
            ServiceResult<T> result = new ServiceResult<T>();
            try
            {
                HttpClient http = new HttpClient();
                HttpItem item = new HttpItem()
                {
                    Method = "get",
                    ContentType = "application/json,*/*"
                };
                if (prepareHttpItem != null)
                    prepareHttpItem(item);
                HttpResult httpResult = http.GetHtml(item);
                result.HttpResult = httpResult;
                if (httpResult.StatusCode == HttpStatusCode.OK)
                {
                    if (checkError != null)
                    {
                        if (checkError(httpResult))
                        {
                            result.Results = httpResult.Html.JsonToObject<T>();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                result.Exception = ex;
            }
            return result;
        }
    }
}